package calcutil

import (
	"encoding/hex"
	"github.com/shopspring/decimal"
	"math/big"
	"strings"
	"wzgames/internal/constant"
	"wzgames/internal/model/mcomm"
	"wzgames/internal/response"
	"wzgames/pkg/utils/stringutil"
)

var (
	BaseCoefficient = decimal.NewFromInt(100)
	OneF            = decimal.NewFromFloat(1.0)
)

func Hex2Dec(hexv string) (int64, error) {
	byteValue, err := hex.DecodeString(hexv)
	if err != nil {
		return 0, err
	}
	intVal := new(big.Int).SetBytes(byteValue)
	return intVal.Int64(), nil
}

func CalcIntDiv100(v int64) float64 {
	s := decimal.NewFromInt(v)
	r := s.Div(BaseCoefficient)
	balance, _ := r.Float64()
	return balance
}

func RoundFloor(v float64) float64 {
	r1 := decimal.NewFromFloat(v).Mul(BaseCoefficient)
	return CalcIntDiv100(r1.IntPart())
}

func RoundFloorD(v decimal.Decimal) float64 {
	r1 := v.Mul(BaseCoefficient)
	return CalcIntDiv100(r1.IntPart())
}

func CalcWithMultiplier(floatStr string, multiplier float64) (int64, float64) {
	amt, _ := decimal.NewFromString(floatStr)
	multp := decimal.NewFromFloat(multiplier)
	result := amt.Mul(multp).Mul(BaseCoefficient)
	intPart := result.IntPart()
	return intPart, CalcIntDiv100(intPart)
}

func CalcWithMultiplier1(floatStr string, multiplier float64) int64 {
	amt, _ := decimal.NewFromString(floatStr)
	multp := decimal.NewFromFloat(multiplier)
	result := amt.Mul(multp).Mul(BaseCoefficient)
	intPart := result.IntPart()
	return intPart
}

func CalcFloatSub(f1, f2 float64) float64 {
	v1 := decimal.NewFromFloat(f1).Sub(decimal.NewFromFloat(f2))
	r := v1.Mul(BaseCoefficient)
	return CalcIntDiv100(r.IntPart())
}

func CheckAmountDecimalPoint(amt string, precision int32) bool {
	if amt == "" {
		return false
	}
	idx := strings.Index(amt, ".")
	if idx == -1 {
		return true
	}
	if len(amt[idx:]) > int(precision+1) {
		return false
	}
	return true
}

func AmountDecimalPointCut(amt string, precision int32) string {
	if amt == "" {
		return amt
	}
	idx := strings.Index(amt, ".")
	if idx == -1 {
		return amt
	}
	prec := int(precision)

	if len(amt[idx:]) > (prec + 1) {
		if prec > 0 {
			prec += 1
		}
		return amt[0 : idx+prec]
	}
	return amt
}

func SplitAmount(amount string, Precision int32) (int16, int64, int64) {
	if amount == "" {
		return response.ErrAmount, 0, 0
	}
	idx := strings.Index(amount, constant.Dot)
	var s1 string
	var part1 int64
	var part2 int64
	if idx == -1 {
		s1 = amount
	} else {
		s1 = amount[0:idx]
		if s1 == "" {
			s1 = "0"
		}
	}
	part1, _ = stringutil.ToInt64(s1, 0)
	if idx != -1 && Precision > 0 {
		decS := amount[idx:]
		if decS != constant.Dot {
			decS = amount[idx+1:]
			l := int32(len(decS))
			if l > Precision {
				return response.ErrAmount, 0, 0
			}
			part2, _ = stringutil.ToInt64(decS, 0)
			dif := Precision - l
			if dif > 0 {
				part2 *= GetPrecisionFactor(dif)
			}
		}
	}
	if part1 <= 0 && part2 <= 0 {
		return response.ErrAmount, 0, 0
	}

	return 0, part1, part2
}

func IncAmount(wp1, wp2, p1, p2 int64, Precision int32) (int64, int64) {
	np1 := wp1 + p1
	if Precision == 0 {
		return np1, 0
	}

	np2 := wp2 + p2
	PrecisionVal := GetPrecisionFactor(Precision)
	if np2 >= PrecisionVal {
		np1 += 1
		np2 -= PrecisionVal
	}

	return np1, np2
}

func DecAmount(wp1, wp2, p1, p2 int64, Precision int32) (int16, int64, int64) {
	if wp1 < p1 || wp1 == p1 && wp2 < p2 {
		return response.ErrInsufficientBalance, wp1, wp2
	}
	np1 := wp1 - p1
	if wp2 < p2 {
		np1 -= 1
		np2 := (GetPrecisionFactor(Precision) - p2) + wp2
		return 0, np1, np2
	}

	return 0, np1, wp2 - p2
}

func AmountAdd(amt1, amt2 string) (string, bool) {
	d1, _ := decimal.NewFromString(amt1)
	d2, _ := decimal.NewFromString(amt2)
	d := d1.Add(d2)
	return d.String(), d.IsNegative()
}

func AmountSub(amt1, amt2 string) (string, bool) {
	d1, _ := decimal.NewFromString(amt1)
	d2, _ := decimal.NewFromString(amt2)
	d := d1.Sub(d2)
	return d.String(), d.IsNegative()
}

func AmountCompare(amt1, amt2 string) int {
	d1, _ := decimal.NewFromString(amt1)
	d2, _ := decimal.NewFromString(amt2)
	return d1.Cmp(d2)
}

func CheckGameResult(multiplier decimal.Decimal) mcomm.GameResult {
	if multiplier.GreaterThan(OneF) {
		return mcomm.GameResult_GameWin
	} else if multiplier.Equals(OneF) {
		return mcomm.GameResult_GameDraw
	}

	return mcomm.GameResult_GameLose
}
